/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.exec;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jgrapht.ext.DOTExporter;
import org.jgrapht.ext.GmlExporter;
import org.jgrapht.ext.IntegerNameProvider;
import org.jgrapht.ext.StringNameProvider;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DefaultWeightedEdge;

import combi.wip.sipper.io.dot.DOTColorExporter;
import combi.wip.sipper.io.dot.EmptyEdgeNameProvider;
import combi.wip.sipper.io.dot.VertexAlternateNameProvider;
import combi.wip.sipper.io.dot.VertexQuotedNameProvider;

import combi.wip.utils.OrdrePartiel;
import combi.wip.utils.OrdrePartielInclusionSetString;
import combi.wip.graph.Treilli;
import combi.utils.Couple;


public class ComputeKSIPsModules {

	private static String extTemplateGraph = ".dot";
	//private static int k =10;

	public static void main(String[] args) {
		
		String fileIn = args[0];
		String outRep = args[1];
		int a = fileIn.lastIndexOf(".");
		int b = fileIn.length();
		int c = fileIn.lastIndexOf("/");
		if (Math.min(c,a)==a)
		{
			a = b;
		}
		String fileInNameString = fileIn.substring(c, a);
		String fileInNameStringPath = outRep+ fileInNameString;
		String fileFeuilles = fileInNameStringPath + "_treilli_Leaves.txt";
		String pathTemplateGraph = fileInNameStringPath+ "_modules/mod_";
		
		//String fileIn = pathPrefix + "cheminDecontract.txt";
		//String fileOut = pathPrefix + "infoResultat"+ k +"ksp.txt";
		//String fileOutReactions = pathPrefix + "infoResultatReactions"+ k +"ksp.txt";
		//String fileOutGenes = pathPrefix + "infoResultatGenes"+ k +"ksp.txt";
		//String fileBase = ressources + "genesPos.txt";
		//String fileEquiv = pathPrefix + "redondances"+ k +".txt";

		//String dirIn = "/Users/bordronphilippe/Documents/Th�se/GOclusters/pvalue10";
		//String dirIn = pathPrefix+"/pvalue"+ k ;

		try{
			Matcher m;
			//String dd = "(?:[0-9]+(?:\\.[0-9]+)?)";
			String recoReaction = "(?:" +"[^ \t,\\]\\[)(]+" + ")";
			String beginBloc = "(?:(" + recoReaction + ")->(" + recoReaction + "))";
			//String geneName = "(?:(" + flottant + ")\t(" + flottant + ")\t(" + flottant + ")\t" +"\\[\\{\\((?:\\+|-)b[0-9]{4}:([^,:]+),.+:([^,:]+),R[0-9]{5}\\),?\\},?\\]" + ")"; //gloutonnerie
			//String geneName = "(?:"+ "(?:" + ":([^,]+),(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e
			String geneName = "(?:"+ "(?:" + "\\([+-]?([^:]+)[^,]*,(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e

	
			Pattern reco = Pattern.compile(beginBloc);
			Pattern recoGeneName = Pattern.compile(geneName);

			//chargement des �quivalences de clusters de genes pour la pvalue.
			BufferedReader in;
			String ch;
			Couple<String, String> id = null;
			in = new BufferedReader (new FileReader (fileIn));

			Map<Couple<String, String>, List<String>> genesCh = new HashMap<Couple<String, String>, List<String>>();
			Map<List<String>, String> labels = new HashMap<List<String>, String>();

			//OrdrePartiel<List<String>> orderP = new OrdrePartielInclusionChemins();
			OrdrePartiel<List<String>> orderP = new OrdrePartielInclusionSetString();
			Treilli<List<String>> t = new Treilli<List<String>>(orderP);

			List<String> chemin = new LinkedList<String>();
			int limiteK = 0;
			int cpt0 = 0;
			Map<List<String>, String> sauvFic = new HashMap<List<String>,String>();
			String sauvBloc = "";
			while ((ch = in.readLine()) != null)
			{
				try
				{
					if (ch.isEmpty()) //fini
					{
						cpt0++;
						//System.err.println("ch.isEmpty()");
						if (chemin.size() != 0)
						{
							System.err.println(cpt0 + " : " + id);
							genesCh.put(id, chemin);
							if (t.add(chemin))
							{	
								labels.put(chemin,new String());
							}
							labels.put(chemin, labels.get(chemin) + id.getFirst().toString()+"_to_"+id.getSecond().toString() +"\\\\n");
							sauvFic.put(chemin, sauvBloc);

						}
						//raz
						id = null;
						limiteK = -1;
						chemin = new LinkedList<String>();
						sauvBloc = "";
					}
					else
					{
						m = reco.matcher(ch);
						//reconnaissance des Rx -> Ry
						if (m.find())
						{
							id = new Couple<String, String>(new String(m.group(1)),new String(m.group(2)));
							//System.err.println(id);
						}
						System.err.println(id);


						m = recoGeneName.matcher(ch);
						while(m.find())
						{
							Couple<String, String> newCouple = new Couple<String, String> (new String(m.group(1)), new String (m.group(2)));
							//System.err.println(newCouple);
							chemin.add(newCouple.toString());
						}
						limiteK++;
						sauvBloc =sauvBloc + ch + "\n";
					}
				}
				catch (Exception e)
				{
					e.printStackTrace();
				}
				//enregistrement des valeurs extraitent dans structure.
			}
			if (chemin.size() != 0)
			{
				System.err.println(id);
				genesCh.put(id, chemin);
				if (t.add(chemin))
				{	System.err.println(id);
				labels.put(chemin,new String());
				}
				labels.put(chemin, labels.get(chemin) + id.getFirst().toString()+"_to_"+id.getSecond().toString() +"\\\\n");

			}
			in.close();
			System.err.println("Exportation treillis");

			//System.err.println(t.getTreillis());
			for (Entry<List<String>, String> entry : labels.entrySet())
			{
				entry.setValue("\"" + entry.getValue().substring(0, entry.getValue().lastIndexOf("\\")-1) + "\"");
			}

			labels.put(orderP.getFakeRoot(), "\"\"");
			GmlExporter<List<String>, DefaultEdge> graphML = new GmlExporter<List<String>, DefaultEdge>();
			DOTColorExporter<List<String>, DefaultEdge> graphDOT = new DOTColorExporter<List<String>, DefaultEdge>(new IntegerNameProvider<List<String>>(), new VertexAlternateNameProvider<List<String>>(labels),null);
			try {
				graphDOT.setRankDir("LR");
				graphDOT.export(new BufferedWriter(new FileWriter(fileInNameStringPath + "_treilli.dot")), t.getTreillis());
				graphML.export(new BufferedWriter(new FileWriter(fileInNameStringPath + "_treilli.gml")), t.getTreillis());
				//exportation des feuilles du treillis.


			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}


			//Exportation des feuilles.
			System.err.println("Exportation des feuilles du treillis");
			//System.err.println(sauvFic);
			BufferedWriter out;
			try {
				out = new BufferedWriter(new FileWriter(fileFeuilles));
				//System.err.println(t.getTreillis().vertexSet());
				for (List<String> v :t.getTreillis().vertexSet())
				{
					//System.out.println(t.getTreillis().outDegreeOf(v));
					if (t.getTreillis().outDegreeOf(v) == 0)
					{
						//feuille
						out.write(sauvFic.get(v) + "\n");
					}
				}
				out.close();
			}catch(Exception e){
				e.printStackTrace();
				System.exit(1);
			}
			
			//Generation des modules

			
			/*
			 * Calcul des modules a partir des chemins feuilles (non inclus dans d'autres)
			 */

			// charge feuilles Treillis
			Collection<List<List<Couple<String, String>>>> feuilles = null;
			try{
				System.err.println("Chargement des feuilles");
				feuilles = chargeFeuilles(fileFeuilles);
				System.err.println(feuilles.size() + " feuilles");
				/*for (List<Couple<String, String>> f : feuilles)
					System.err.println(f);*/
			}
			catch(Exception e)
			{
				e.printStackTrace();
				System.exit(1);
			}

			// fusionne les chemins avec sommets communs pour obtenir sous-graphes.
			System.err.println("Creation des clusters de feuilles");
			Map<List<List<Couple<String, String>>>, Set<List<List<Couple<String, String>>>>> assoFeuilles = new HashMap<List<List<Couple<String, String>>>, Set<List<List<Couple<String, String>>>>>();
			for (List<List<Couple<String, String>>> f : feuilles)
			{
				Set<List<List<Couple<String, String>>>> identite  = new HashSet<List<List<Couple<String, String>>>>();
				identite.add(f);
				assoFeuilles.put(f,identite);
			}
			//System.err.print(assoFeuilles);


			for (List<List<Couple<String, String>>> ch1 : feuilles)
			{
				for (List<List<Couple<String, String>>> ch2 : feuilles)
				{
					if (!ch1.equals(ch2))
					{
						Set<Couple<String,String>> commun = new HashSet<Couple<String,String>>();
						for (List<Couple<String,String>> p1 : ch1)
						{
							commun.addAll(p1);
						}

						Set<Couple<String,String>> retain = new HashSet<Couple<String,String>>();
						for (List<Couple<String,String>> p2 : ch2)
						{
							retain.addAll(p2);
						}

						commun.retainAll(retain);
						if (!commun.isEmpty())
						{
							//System.err.println("\t" + ch1 + "\n\t"+ ch2);
							Set<List<List<Couple<String, String>>>> temp1 = assoFeuilles.get(ch1);
							Set<List<List<Couple<String, String>>>> temp2 = assoFeuilles.get(ch2);
							temp1.addAll(temp2);
							for (List<List<Couple<String, String>>> f : temp2)
							{
								assoFeuilles.put(f, temp1);
							}
							//temp2.clear();
						}
						retain.clear();
					}
				}
			}		
			//generation des sous-graphes
			Collection<Set<List<List<Couple<String, String>>>>> clustersDeChemins = new HashSet<Set<List<List<Couple<String, String>>>>>(assoFeuilles.values());
			System.err.println(clustersDeChemins.size() + " clusters de chemins");
			//System.err.print(assoFeuilles);

			Collection<DefaultDirectedWeightedGraph<Couple<String, String>, DefaultWeightedEdge>> graphes = new ArrayList<DefaultDirectedWeightedGraph<Couple<String, String>, DefaultWeightedEdge>>(clustersDeChemins.size());

			System.err.println("Generation des sous-graphes");
			for(Set<List<List<Couple<String, String>>>> kchemins : clustersDeChemins)
			{
				DefaultDirectedWeightedGraph<Couple<String, String>, DefaultWeightedEdge> graph = new DefaultDirectedWeightedGraph<Couple<String, String>, DefaultWeightedEdge>(DefaultWeightedEdge.class);
				for (List<List<Couple<String, String>>> chemins : kchemins)
				{
					for (List<Couple<String, String>> f : chemins)
					{
						Couple<String, String> v1 = null;
						for (Couple<String, String> v2 : f)
						{
							if(! graph.containsVertex(v2))
							{
								graph.addVertex(v2);
							}
							if (v1 != null && !graph.containsEdge(v1, v2))
							{
								graph.addEdge(v1, v2);
								//graph.setEdgeWeight(graph.getEdge(v1, v2), poids);
							}
							v1 = v2; 
						}
					}
					chemins.clear();
				}
				graphes.add(graph);
				kchemins.clear();
			}

			clustersDeChemins.clear();
			// export des sous-graphes

			System.err.println("Export des " + graphes.size() + " sous-graphes");
			int cpt = 0;
			File f = new File(pathTemplateGraph + cpt + extTemplateGraph);
			// tester si le parent (qui est un répertoire existe)
			File p = f.getParentFile(); // me souvient plus de la méthode
			if (!p.exists())
			{
				p.mkdirs(); // créer le rep
			}
			else
			{
				for (File temp : p.listFiles())
				{
					//System.err.println(temp);
					temp.delete();
				}
			}
			for (DefaultDirectedWeightedGraph<Couple<String, String>, DefaultWeightedEdge> graph : graphes)
			{
				cpt++ ;
				System.out.println(graph);
				//DOTExporter<Couple<String,String>, DefaultWeightedEdge> outModele = new DOTExporter<Couple<String,String>, DefaultWeightedEdge>(new IntegerNameProvider<Couple<String,String>>(), new VertexQuotedNameProvider<Couple<String,String>>(),new EmptyEdgeNameProvider<DefaultWeightedEdge>());
				DOTColorExporter<Couple<String,String>, DefaultWeightedEdge> outModele = new DOTColorExporter<Couple<String,String>, DefaultWeightedEdge>(new IntegerNameProvider<Couple<String,String>>(), new VertexQuotedNameProvider<Couple<String,String>>(),null);
				try {

					outModele.export(new BufferedWriter(new FileWriter(pathTemplateGraph + cpt + extTemplateGraph)), graph);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			
			
		}catch(Exception e){
			e.printStackTrace();
			System.exit(1);
		}

		finally{

		}
	}

	public static Collection<List<List<Couple<String, String>>>> chargeFeuilles(String fileIn) throws IOException
	{		
		Collection<List<List<Couple<String, String>>>> result = new LinkedList<List<List<Couple<String, String>>>>();

		String ch;
		BufferedReader in;
		in = new BufferedReader (new FileReader(fileIn));
		String buffer = "";
		while ((ch = in.readLine()) != null)
		{
			if (ch.isEmpty()) //fini
			{
				List<List<Couple<String, String>>> chemins = analyseBloc(buffer);
				if (!chemins.isEmpty())
				{
					//TODO passage a k>=1
					result.add(chemins);
				}
				buffer = "";
			}
			else{
				buffer = buffer + "\n" + ch;
			}
		}
		in.close();
		return result;
	}

	
	private static List<List<Couple<String, String>>> analyseBloc(String bloc) {
		List<List<Couple<String, String>>> result = new LinkedList<List<Couple<String, String>>>();
		Matcher m;
		String recoReaction = "(?:" +"[^ \t,\\]\\[)(]+" + ")";
		String beginBloc = "(?:(" + recoReaction + ")->(" + recoReaction + "))";
		String sommetChemin = "(?:"+ "(?:" + "\\([+-]?([^:]+)[^,]*,(" + recoReaction  + ")\\))" + ")"; //gloutonnerie limit�e
		Pattern recoBloc = Pattern.compile(beginBloc);
		Pattern recoSommetChemin = Pattern.compile(sommetChemin);
		boolean estBloc = false;
		int cpt = 0;
		for (String s : bloc.split("\n"))
		{
			try
			{
				if (cpt == 0)
				{
					m = recoBloc.matcher(s);
					estBloc = estBloc || m.find();
				}
				else
				{
					m = recoSommetChemin.matcher(s);
					LinkedList<Couple<String,String>> path = new LinkedList<Couple<String,String>>();
					while(m.find())
					{
						Couple<String, String> v = new Couple<String,String>(m.group(1), m.group(2));
						path.addLast(v);
					}
					if (!path.isEmpty())
					{
						result.add(path);
					}
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
			cpt++;
		}

		return result;
	}
}
